<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import { CreateElement, VNode } from 'vue'
import { importDefaultModuleAll } from '@/utils'
import ImageViewer from '../file-operation/ImageViewer.vue'

const baseItems = importDefaultModuleAll(
  require.context('./base-table-items', false, /\.vue$/)
)

const itemMap: { [key: string]: string } = {
  default: 'el-table-column',
  action: 'RenderAction',
  date: 'RenderDate',
  image: 'RenderImage',
  link: 'RenderLink',
  money: 'RenderMoney',
  popover: 'RenderPopover',
  tag: 'RenderTag'
}

@Component({
  name: 'RenderTable',
  components: { ...baseItems, ImageViewer }
})
export default class extends Vue {
  // table data
  @Prop({ type: Array, required: false, default: () => [] })
  private data!: Array<any>
  // table column list
  @Prop({ type: Array, required: false, default: () => [] })
  private columns!: Array<any>
  // whether to display table selection
  @Prop({ type: Boolean, required: false, default: false })
  private showSelection!: boolean
  // loading state
  @Prop({ type: Boolean, required: false, default: false })
  private loading!: boolean
  // table pagination
  @Prop({ type: Object, required: false })
  private page!: { pageNo: number; pageSize: number; total: number }

  private tableMaxHeight = 0
  private pageHeight = 50
  private showPreview = false
  private previewURL = ''

  get hasImageViewer () {
    return this.columns.some(_ => !_.hide && _.type === 'image')
  }

  mounted () {
    this.$nextTick(() => {
      this.calcTableMaxHeight()
    })
  }

  private calcTableMaxHeight (): void {
    // DOM action
    let parentDOM = this.$parent.$el as HTMLElement
    let parentHeight = this.calcDOMInnerHeight(parentDOM)

    for (const element of parentDOM.children) {
      if (!element.classList.contains('normal-table-wrap')) {
        let elementHeight = this.calcDOMOuterHeight(element as HTMLElement)
        parentHeight -= elementHeight
      }
    }

    this.tableMaxHeight = parentHeight - (this.page ? this.pageHeight : 0)
  }
  private calcDOMInnerHeight (dom: HTMLElement): number {
    if ('getComputedStyle' in window) {
      let style = window.getComputedStyle(dom, null)

      return Math.floor(
        dom.getBoundingClientRect().height -
          parseFloat(style.getPropertyValue('padding-top') || '0') -
          parseFloat(style.getPropertyValue('padding-bottom') || '0') -
          parseFloat(style.getPropertyValue('border-top-width') || '0') -
          parseFloat(style.getPropertyValue('border-bottom-width') || '0')
      )
    } else {
      return dom.clientHeight - 1
    }
  }
  private calcDOMOuterHeight (dom: HTMLElement): number {
    if ('getComputedStyle' in window) {
      let style = window.getComputedStyle(dom, null)

      return Math.ceil(
        dom.getBoundingClientRect().height +
          parseFloat(style.getPropertyValue('margin-top') || '0') +
          parseFloat(style.getPropertyValue('margin-bottom') || '0')
      )
    } else {
      return dom.offsetHeight + 1
    }
  }
  private handlePageChange (pageNo: number, pageSize: number): void {
    if (Math.ceil(this.page.total / pageSize) >= pageNo) {
      this.$emit('page-change', pageNo, pageSize)
    }
  }
  private handlePreview (url: string): void {
    this.previewURL = url
    this.showPreview = true
  }

  render (h: CreateElement) {
    const selection = h('el-table-column', {
      props: { type: 'selection', width: '40', fixed: 'left' }
    })

    const imageViewer = h('ImageViewer', {
      props: { visible: this.showPreview, src: this.previewURL },
      on: {
        'update:visible': (v: boolean) => {
          this.showPreview = v
        }
      }
    })

    let tableColumns = this.columns.reduce((acc: Array<VNode>, _) => {
      if (!_.hide) {
        let obj = { ..._ }
        let on

        if (_.attrs) {
          Object.assign(obj, _.attrs)
          delete obj.attrs
        }

        if (!_.type) {
          obj.type = 'default'
        }

        if (_.type === 'image') {
          on = { preview: this.handlePreview }
        }

        let columnItem = h(itemMap[obj.type], {
          attrs: obj,
          props: _.attrs,
          on
        })

        acc.push(columnItem)
      }
      return acc
    }, [])

    if (this.showSelection) {
      tableColumns.unshift(selection)
    }

    let table = h(
      'el-table',
      {
        props: {
          data: this.data,
          'max-height': this.tableMaxHeight,
          stripe: true,
          border: true,
          'highlight-current-row': true,
          ...this.$attrs
        },
        on: this.$listeners,
        directives: [
          { name: 'loading', value: this.loading, modifiers: { lock: true } }
        ]
      },
      tableColumns
    )

    return h(
      'div',
      { class: 'normal-table-wrap' },
      this.hasImageViewer ? [table, imageViewer] : [table]
    )
  }
}
</script>
